package ca.uhn.hl7v2.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ca.uhn.hl7v2.util.idgenerator.FileBasedHiLoGenerator;

/**
 * <p>
 * Creates unique message IDs.  IDs are stored in a file called {@link Home#getHomeDirectory() hapi.home}/id_file for persistence
 * across JVM sessions.  Note that if one day you run the JVM with a new working directory,
 * you must move or copy id_file into this directory so that new ID numbers will begin
 * with the last one used, rather than starting over again.
 * </p>
 * <p>
 * Note that as of HAPI 2.0, by default this class will not fail even if the id_file can
 * not be read/written. In this case, HAPI will try to fail gracefully by simply generating
 * a numeric sequence starting at zero. This behaviour can be overwritten using 
 * {@link #NEVER_FAIL_PROPERTY}
 * </p>
 * Note: you should not use this class directory, but use the {@link IDGeneratorFactory} instead. 
 * Also consider using {@link FileBasedHiLoGenerator} which provides better performance
 * 
 * 
 * @author Neal Acharya
 * @deprecated use one of the IDGenerator implementations
 */
public class MessageIDGenerator {
    
	private static final Logger ourLog = LoggerFactory.getLogger(MessageIDGenerator.class.getName());
    private static MessageIDGenerator messageIdGenerator;
    
    /**
     * Contains the complete path to the default ID file, which is a plain text file containing
     * the number corresponding to the last generated ID
     */
    public final static String DEFAULT_ID_FILE = Home.getHomeDirectory().getAbsolutePath() + "/id_file";
    
    /**
     * System property key which indicates that this class should never fail. If this
     * system property is set to false (default is true), as in the following code:<br>
     * <code>System.setProperty(MessageIDGenerator.NEVER_FAIL_PROPERTY, Boolean.FALSE.toString());</code><br>
     * this class will fail if the underlying disk file can not be
     * read or written. This means you are roughly guaranteed a unique
     * ID number between JVM sessions (barring the file getting lost or corrupted). 
     */
    public static final String NEVER_FAIL_PROPERTY = MessageIDGenerator.class.getName() + "_NEVER_FAIL_PROPERTY";
    
    private long id;
    private FileWriter fileW;
    
    /**
     * Constructor
     * Creates an instance of the class
     * Its reads an id (longint#) from an external file, if one is not present then the external file
     * is created and initialized to zero.
     * This id is stored into the private field of id.
     */
    private  MessageIDGenerator() throws IOException {
        initialize();
    }//end constructor code

    
	/**
	 * Force the generator to re-load the ID file and initialize itself.
	 * 
	 * This method is mostly provided as a convenience to unit tests, and does
	 * not normally need to be called.
	 */
    void initialize() throws IOException {
    	id = 0;
    	
		/*check external file for the last value unique id value generated by
        this class*/
        try{
            // We should check to see if the external file for storing the unique ids exists
            File extFile = new File(DEFAULT_ID_FILE);
            if (extFile.createNewFile()== true){
                /*there was no existing file so a new one has been created with createNewFile method.  The
                file is stored at  <hapi.home>/id_file.txt */
                // We can simply initialize the private id field to zero
                id = 0;
                
            }//end if
            else{
                /*The file does exist which is why we received false from the
                createNewFile method. We should now try to read from this file*/
                FileReader fileR = new FileReader(DEFAULT_ID_FILE);
                char[] charArray = new char[100];
                int e = fileR.read(charArray);
                if (e <= 0){
                    /*We know the file exists but it has no value stored in it. So at this point we can simply initialize the
                    private id field to zero*/
                    id = 0;
                }//end if
                else{
                    /* Here we know that the file exists and has a stored value. we should read this value and set the
                    private id field to it*/
                    String idStr = String.valueOf(charArray);
                    String idStrTrim = idStr.trim();
                    
                    try {
                    	id = Long.parseLong(idStrTrim);
                    } catch (NumberFormatException nfe) {
                    	ourLog.warn("Failed to parse message ID file value \"" + idStrTrim + "\". Defaulting to 0.");
                    }
                    
                }//end else
                //Fix for bug 1100881:  Close the file after writing.
                fileR.close();
            }//end else
        } catch (FileNotFoundException e) {
            ourLog.error("Failed to locate message ID file. Message was: {}", e.getMessage());
        } catch (IOException e) {
        	if (Boolean.TRUE.equals(System.getProperty(NEVER_FAIL_PROPERTY, Boolean.TRUE.toString()))) {
        		ourLog.warn("Could not retrieve message ID file, going to default to ID of 0. Message was: {}", e.getMessage());
        		id = 0;
        		return;
        	} else {
        		throw e;
        	}
        }
	}
    
    /**
     * Synchronized method used to return the single (static) instance of the class
     */
    public static synchronized MessageIDGenerator getInstance() throws IOException {
        if (messageIdGenerator == null)
            messageIdGenerator = new MessageIDGenerator();
        return messageIdGenerator;
    }//end method
    
    /**
     * Synchronized method used to return the incremented id value
     */
    public synchronized String getNewID() throws IOException{
    	try {
	    	//increment the private field
	        id = id + 1;
	        //write the id value to the file
	        String idStr = String.valueOf(id);

	        //create an instance of the Filewriter Object pointing to "C:\\extfiles\\Idfile.txt"
	        fileW = new FileWriter(DEFAULT_ID_FILE, false);
	        fileW.write(idStr);
	        fileW.flush();
	        fileW.close();
    	} catch (FileNotFoundException e) {
        	if (Boolean.TRUE.equals(System.getProperty(NEVER_FAIL_PROPERTY, Boolean.TRUE.toString()))) {
        		ourLog.info("Failed to create message ID file. Message was: {}", e.getMessage());
        		fileW = null;
        	}
    	} catch (IOException e) {
        	if (Boolean.TRUE.equals(System.getProperty(NEVER_FAIL_PROPERTY, Boolean.TRUE.toString()))) {
        		ourLog.debug("Failed to create message ID file. Message was: {}", e.getMessage());
        		fileW = null;
        	} else {
        		throw e;
        	}
    	}
    	return String.valueOf(id);
    }//end method
    
}
